#include "CCanvas.h"
#include "CSlideRule.h"

BEGIN_EVENT_TABLE(CCanvas, wxScrolledWindow)
EVT_PAINT(CCanvas::OnPaint)
EVT_LEFT_DOWN(CCanvas::OnLeftButtonDown)
EVT_MOUSE_EVENTS(CCanvas::OnMouseDrag)
EVT_LEFT_UP(CCanvas::OnLeftButtonUp)
EVT_KEY_UP(CCanvas::OnKeyUp)
END_EVENT_TABLE()

CCanvas::CCanvas(wxWindow *lpParentFrame): wxScrolledWindow(lpParentFrame)
{
    int i;
    m_nFrameWidth = (unsigned int)((TICK_LEFT - RULE_LEFT) * 2 + MAX_TICK_INTERVAL * MAX_TICK_DENSITY * std::log(100.0));
    m_nAuxRuleTop = MAIN_RULE_FRAME_TOP + UPPER_FRAME_HEIGHT;
    m_nAuxRuleBottom = m_nAuxRuleTop + AUX_HEIGHT;
    m_nEntireHeight = UPPER_FRAME_HEIGHT + AUX_HEIGHT + DOWN_FRAME_HEIGHT;
    m_nMainUpperTickMark = m_nAuxRuleTop - TICK_LENGTH1;
    m_nMainDownTickMark = m_nAuxRuleBottom + TICK_LENGTH1;
    m_nAuxTextMid = m_nAuxRuleTop + AUX_HEIGHT / 2;
    m_lpiTicks1 = new int[10];
    for (i = 0; i < 10; ++i)
    {
        m_lpiTicks1[i] = int(TICK_LEFT + slide_rule.m_lpdblTicks1[i] * MAX_TICK_DENSITY * MAX_TICK_INTERVAL + 0.5);
    }
    m_lpiTicks2 = new int[12];
    for (i = 0; i < 12; ++i)
    {
        m_lpiTicks2[i] = int(TICK_LEFT + slide_rule.m_lpdblTicks2[i] * MAX_TICK_DENSITY * MAX_TICK_INTERVAL + 0.5);
    }
    m_lpiTicks3 = new int[50];
    for (i = 0; i < 50; ++i)
    {
        m_lpiTicks3[i] = int(TICK_LEFT + slide_rule.m_lpdblTicks3[i] * MAX_TICK_DENSITY * MAX_TICK_INTERVAL + 0.5);
    }
    m_lpiTicks4 = new int[80];
    for (i = 0; i < 80; ++i)
    {
        m_lpiTicks4[i] = int(TICK_LEFT + slide_rule.m_lpdblTicks4[i] * MAX_TICK_DENSITY * MAX_TICK_INTERVAL + 0.5);
    }
    m_lpiTicks5 = new int[30];
    for (i = 0; i < 30; ++i)
    {
        m_lpiTicks5[i] = int(TICK_LEFT + slide_rule.m_lpdblTicks5[i] * MAX_TICK_DENSITY * MAX_TICK_INTERVAL + 0.5);
    }
    m_iAuxPos = int(MAX_TICK_DENSITY * MAX_TICK_INTERVAL * log(slide_rule.m_iFactor / 100.0) + 0.5);
    m_iVernier = int(MAX_TICK_DENSITY * MAX_TICK_INTERVAL * log(slide_rule.m_iProduct / 10000.0) + 0.5);
    m_iSelect = 0;
}

CCanvas::~CCanvas()
{
    delete []m_lpiTicks5;
    delete []m_lpiTicks4;
    delete []m_lpiTicks3;
    delete []m_lpiTicks2;
    delete []m_lpiTicks1;
}


void CCanvas::OnSize(wxSizeEvent &event)
{
    ;
}

void CCanvas::OnPaint(wxPaintEvent &event)
{
    wxPaintDC dc(this);
    dc.SetBackground(*wxWHITE_BRUSH);
    dc.Clear();
    DrawMainRuleFrame(dc);
    DrawAuxRuleFrame(dc);
    DrawMainRuleTicks(dc);
    DrawAuxRuleTicks(dc);
    DrawMainRuleTickMarks(dc);
    DrawAuxRuleTickMarks(dc);
    DrawEquation(dc);
}

void CCanvas::OnLeftButtonDown(wxMouseEvent &event)
{
    event.GetPosition(&m_wxcX, &m_wxcY);
    if (m_wxcY > MAIN_RULE_FRAME_TOP && m_wxcY < wxCoord(MAIN_RULE_FRAME_TOP + m_nEntireHeight) && m_wxcX >= wxCoord(TICK_LEFT + m_iVernier - VERNIER_RADIUS) && m_wxcX <= wxCoord(TICK_LEFT + m_iVernier + VERNIER_RADIUS))
    {
        m_iSelect = 2;
    }
    else if(m_wxcY > wxCoord(m_nAuxRuleTop) && m_wxcY < wxCoord(m_nAuxRuleBottom) && m_wxcX > wxCoord(RULE_LEFT + m_iAuxPos) && m_wxcX < wxCoord(RULE_LEFT + m_nFrameWidth + m_iAuxPos))
    {
        m_iSelect = 1;
    }
    else
    {
        m_iSelect = 0;
    }
}

void CCanvas::OnMouseDrag(wxMouseEvent &event)
{
    wxCoord x, y;
    int iOldAuxPos, iOldVernier, iMinX, iMaxX;
    wxRect lpwxrectRefresh[2];
    if (m_iSelect != 0 && event.Dragging())
    {
        event.GetPosition(&x, &y);
        if (m_iSelect == 1)
        {
            slide_rule.m_iFactor = int(std::exp(double(m_iAuxPos + (x - m_wxcX)) / (MAX_TICK_DENSITY * MAX_TICK_INTERVAL)) * 100 + 0.5);
            if (slide_rule.m_iFactor > slide_rule.m_iProduct / 100)
            {
                slide_rule.m_iFactor = slide_rule.m_iProduct / 100;
            }
            if (slide_rule.m_iFactor < 100)
            {
                slide_rule.m_iFactor = 100;
            }
            iOldAuxPos = m_iAuxPos;
            m_iAuxPos = int(MAX_TICK_DENSITY * MAX_TICK_INTERVAL * log(slide_rule.m_iFactor / 100.0) + 0.5);
            m_wxcX = x;
            m_wxcY = y;
            iMaxX = std::max(iOldAuxPos, m_iAuxPos);
            iMinX = std::min(iOldAuxPos, m_iAuxPos);
            lpwxrectRefresh[0] = wxRect(iMinX + RULE_LEFT - 2, m_nAuxRuleTop - 2, m_nFrameWidth + iMaxX - iMinX + 4, AUX_HEIGHT + 4);
            Refresh(true, lpwxrectRefresh);

        }
        else if (m_iSelect == 2)
        {
            slide_rule.m_iProduct = int(std::exp(double(m_iVernier + (x - m_wxcX)) / (MAX_TICK_DENSITY * MAX_TICK_INTERVAL)) * 10000 + 0.5);
            if (slide_rule.m_iProduct < slide_rule.m_iFactor * 100)
            {
                slide_rule.m_iProduct = slide_rule.m_iFactor * 100;
            }
            if (slide_rule.m_iProduct > 1000000)
            {
                slide_rule.m_iProduct = 1000000;
            }
        }
        slide_rule.m_iProduct = (slide_rule.m_iProduct + slide_rule.m_iFactor / 2) / slide_rule.m_iFactor * slide_rule.m_iFactor;
        if (slide_rule.m_iProduct > 1000000)
        {
            slide_rule.m_iProduct = 1000000 / slide_rule.m_iFactor * slide_rule.m_iFactor;
        }
        iOldVernier = m_iVernier;
        m_iVernier = int(MAX_TICK_DENSITY * MAX_TICK_INTERVAL * log(slide_rule.m_iProduct / 10000.0) + 0.5);
        m_wxcX = x;
        m_wxcY = y;
        iMinX = std::min(iOldVernier, m_iVernier);
        iMaxX = std::max(iOldVernier, m_iVernier);
        if (iMaxX - iMinX <= 2 * VERNIER_RADIUS)
        {
            lpwxrectRefresh[0] = wxRect(TICK_LEFT + iMinX - VERNIER_RADIUS - 2, MAIN_RULE_FRAME_TOP - 2, (iMaxX - iMinX) + VERNIER_RADIUS * 2 + 4, m_nEntireHeight + 4);
            Refresh(false, lpwxrectRefresh);
        }
        else
        {
            lpwxrectRefresh[0] = wxRect(TICK_LEFT + iMinX - VERNIER_RADIUS - 2, MAIN_RULE_FRAME_TOP - 2, VERNIER_RADIUS * 2 + 4, m_nEntireHeight + 2);
            lpwxrectRefresh[1] = wxRect(TICK_LEFT + iMaxX - VERNIER_RADIUS - 2, MAIN_RULE_FRAME_TOP - 2, VERNIER_RADIUS * 2 + 4, m_nEntireHeight + 2);
            Refresh(false, lpwxrectRefresh);
            Refresh(false, lpwxrectRefresh + 1);
        }
        lpwxrectRefresh[0] = wxRect(RULE_LEFT, 10, m_nFrameWidth, MAIN_RULE_FRAME_TOP - 20);
        Refresh(false, lpwxrectRefresh);
    }
}

void CCanvas::OnLeftButtonUp(wxMouseEvent &event)
{
    m_iSelect = 0;
}

void CCanvas::OnKeyUp(wxKeyEvent &event)
{
    Refresh();
}

void CCanvas::DrawMainRuleFrame(wxPaintDC &dc)
{
    dc.SetPen(*wxBLACK_PEN);
    dc.SetBrush(*wxWHITE_BRUSH);
    dc.DrawRectangle(RULE_LEFT, MAIN_RULE_FRAME_TOP, m_nFrameWidth, m_nEntireHeight);
    dc.SetBrush(*wxLIGHT_GREY_BRUSH);
    dc.DrawRectangle(RULE_LEFT, m_nAuxRuleTop, m_nFrameWidth, AUX_HEIGHT);
}


void CCanvas::DrawAuxRuleFrame(wxPaintDC &dc)
{
    dc.SetPen(*wxBLACK_PEN);
    dc.SetBrush(*wxWHITE_BRUSH);
    dc.DrawRectangle(RULE_LEFT + m_iAuxPos, m_nAuxRuleTop, m_nFrameWidth, AUX_HEIGHT);
    dc.SetPen(*wxGREEN_PEN);
    dc.SetBrush(wxNullBrush);
    dc.DrawRectangle(TICK_LEFT + m_iVernier - VERNIER_RADIUS, MAIN_RULE_FRAME_TOP, VERNIER_RADIUS * 2, m_nEntireHeight);
}

void CCanvas::DrawMainRuleTicks(wxPaintDC &dc)
{
    int i;
    dc.SetPen(*wxBLACK_PEN);
    dc.DrawLine(TICK_LEFT, m_nAuxRuleTop, TICK_LEFT, m_nAuxRuleTop - TICK_LENGTH1);
    dc.DrawLine(TICK_LEFT, m_nAuxRuleBottom, TICK_LEFT, m_nAuxRuleBottom + TICK_LENGTH1);
    for (i = 0; i < 10; ++i)
    {
        dc.DrawLine(m_lpiTicks1[i], m_nAuxRuleTop, m_lpiTicks1[i], m_nAuxRuleTop - TICK_LENGTH1);
        dc.DrawLine(m_lpiTicks1[i], m_nAuxRuleBottom, m_lpiTicks1[i], m_nAuxRuleBottom + TICK_LENGTH1);
    }
    for (i = 0; i < 12; ++i)
    {
        dc.DrawLine(m_lpiTicks2[i], m_nAuxRuleTop, m_lpiTicks2[i], m_nAuxRuleTop - TICK_LENGTH2);
        dc.DrawLine(m_lpiTicks2[i], m_nAuxRuleBottom, m_lpiTicks2[i], m_nAuxRuleBottom + TICK_LENGTH2);
    }
    for (i = 0; i < 50; ++i)
    {
        dc.DrawLine(m_lpiTicks3[i], m_nAuxRuleTop, m_lpiTicks3[i], m_nAuxRuleTop - TICK_LENGTH3);
        dc.DrawLine(m_lpiTicks3[i], m_nAuxRuleBottom, m_lpiTicks3[i], m_nAuxRuleBottom + TICK_LENGTH3);
    }
    for (i = 0; i < 80; ++i)
    {
        dc.DrawLine(m_lpiTicks4[i], m_nAuxRuleTop, m_lpiTicks4[i], m_nAuxRuleTop - TICK_LENGTH4);
        dc.DrawLine(m_lpiTicks4[i], m_nAuxRuleBottom, m_lpiTicks4[i], m_nAuxRuleBottom + TICK_LENGTH4);
    }
    for (i = 0; i < 30; ++i)
    {
        dc.DrawLine(m_lpiTicks5[i], m_nAuxRuleTop, m_lpiTicks5[i], m_nAuxRuleTop - TICK_LENGTH5);
        dc.DrawLine(m_lpiTicks5[i], m_nAuxRuleBottom, m_lpiTicks5[i], m_nAuxRuleBottom + TICK_LENGTH5);
    }
}


void CCanvas::DrawAuxRuleTicks(wxPaintDC &dc)
{
    int i;
    dc.SetPen(*wxBLACK_PEN);
    dc.DrawLine(TICK_LEFT + m_iAuxPos, m_nAuxRuleTop, TICK_LEFT + m_iAuxPos, m_nAuxRuleTop + TICK_LENGTH1);
    dc.DrawLine(TICK_LEFT + m_iAuxPos, m_nAuxRuleBottom, TICK_LEFT + m_iAuxPos, m_nAuxRuleBottom - TICK_LENGTH1);
    for (i = 0; i < 10; ++i)
    {
        dc.DrawLine(m_lpiTicks1[i] + m_iAuxPos, m_nAuxRuleTop, m_lpiTicks1[i] + m_iAuxPos, m_nAuxRuleTop + TICK_LENGTH1);
        dc.DrawLine(m_lpiTicks1[i] + m_iAuxPos, m_nAuxRuleBottom, m_lpiTicks1[i] + m_iAuxPos, m_nAuxRuleBottom - TICK_LENGTH1);
    }
    for (i = 0; i < 12; ++i)
    {
        dc.DrawLine(m_lpiTicks2[i] + m_iAuxPos, m_nAuxRuleTop, m_lpiTicks2[i] + m_iAuxPos, m_nAuxRuleTop + TICK_LENGTH2);
        dc.DrawLine(m_lpiTicks2[i] + m_iAuxPos, m_nAuxRuleBottom, m_lpiTicks2[i] + m_iAuxPos, m_nAuxRuleBottom - TICK_LENGTH2);
    }
    for (i = 0; i < 50; ++i)
    {
        dc.DrawLine(m_lpiTicks3[i] + m_iAuxPos, m_nAuxRuleTop, m_lpiTicks3[i] + m_iAuxPos, m_nAuxRuleTop + TICK_LENGTH3);
        dc.DrawLine(m_lpiTicks3[i] + m_iAuxPos, m_nAuxRuleBottom, m_lpiTicks3[i] + m_iAuxPos, m_nAuxRuleBottom - TICK_LENGTH3);
    }
    for (i = 0; i < 80; ++i)
    {
        dc.DrawLine(m_lpiTicks4[i] + m_iAuxPos, m_nAuxRuleTop, m_lpiTicks4[i] + m_iAuxPos, m_nAuxRuleTop + TICK_LENGTH4);
        dc.DrawLine(m_lpiTicks4[i] + m_iAuxPos, m_nAuxRuleBottom, m_lpiTicks4[i] + m_iAuxPos, m_nAuxRuleBottom - TICK_LENGTH4);
    }
    for (i = 0; i < 30; ++i)
    {
        dc.DrawLine(m_lpiTicks5[i] + m_iAuxPos, m_nAuxRuleTop, m_lpiTicks5[i] + m_iAuxPos, m_nAuxRuleTop + TICK_LENGTH5);
        dc.DrawLine(m_lpiTicks5[i] + m_iAuxPos, m_nAuxRuleBottom, m_lpiTicks5[i] + m_iAuxPos, m_nAuxRuleBottom - TICK_LENGTH5);
    }
}

void CCanvas::DrawMainRuleTickMarks(wxPaintDC &dc)
{
    int i;
    wxCoord width, height;
    wxString wxstrText;
    dc.SetFont(wxFont(TICK_FONT_SIZE, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
    wxstrText = _("1");
    dc.GetTextExtent(wxstrText, &width, &height);
    dc.SetTextForeground(*wxBLUE);
    dc.DrawText(wxstrText, TICK_LEFT - width / 2, m_nMainUpperTickMark - height);
    dc.DrawText(wxstrText, TICK_LEFT - width / 2, m_nMainDownTickMark);
    wxstrText.clear();
    for (i = 0; i < 10; ++i)
    {
        if (i % 2 == 0)
        {
            dc.SetTextForeground(*wxRED);
        }
        else
        {
            dc.SetTextForeground(*wxBLUE);
        }
        wxstrText = wxString::Format("%d", (i + 1) * 10);
        dc.GetTextExtent(wxstrText, &width, &height);
        dc.DrawText(wxstrText, m_lpiTicks1[i] - width / 2, m_nMainUpperTickMark - height);
        dc.DrawText(wxstrText, m_lpiTicks1[i] - width / 2, m_nMainDownTickMark);
        wxstrText.clear();
    }
    for (i = 0; i < 8; ++i)
    {
        wxstrText = wxString::Format("%d", i + 2);
        dc.GetTextExtent(wxstrText, &width, &height);
        dc.SetTextForeground(*wxBLACK);
        dc.DrawText(wxstrText, m_lpiTicks2[i + 4] - width / 2, m_nMainUpperTickMark - height);
        dc.DrawText(wxstrText, m_lpiTicks2[i + 4] - width / 2, m_nMainDownTickMark);
    }

}




void CCanvas::DrawAuxRuleTickMarks(wxPaintDC &dc)
{
    int i;
    wxCoord width, height;
    wxString wxstrText;
    dc.SetFont(wxFont(TICK_FONT_SIZE, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
    wxstrText = _("1");
    dc.GetTextExtent(wxstrText, &width, &height);
    dc.SetTextForeground(*wxBLUE);
    dc.DrawText(wxstrText, TICK_LEFT + m_iAuxPos - width / 2, m_nAuxTextMid - height / 2);
    wxstrText.clear();
    for (i = 0; i < 10; ++i)
    {
        if (i % 2 == 0)
        {
            dc.SetTextForeground(*wxRED);
        }
        else
        {
            dc.SetTextForeground(*wxBLUE);
        }
        wxstrText = wxString::Format("%d", (i + 1) * 10);
        dc.GetTextExtent(wxstrText, &width, &height);
        dc.DrawText(wxstrText, m_lpiTicks1[i] + m_iAuxPos - width / 2, m_nAuxTextMid - height / 2);
        wxstrText.clear();
    }
    for (i = 0; i < 8; ++i)
    {
        wxstrText = wxString::Format("%d", i + 2);
        dc.GetTextExtent(wxstrText, &width, &height);
        dc.SetTextForeground(*wxBLACK);
        dc.DrawText(wxstrText, m_lpiTicks2[i + 4] + m_iAuxPos - width / 2, m_nAuxTextMid - height / 2);
    }
    dc.SetPen(*wxRED_PEN);
    dc.DrawLine(TICK_LEFT + m_iVernier, MAIN_RULE_FRAME_TOP, TICK_LEFT + m_iVernier, MAIN_RULE_FRAME_TOP + m_nEntireHeight);
}

void CCanvas::DrawEquation(wxPaintDC &dc)
{
    wxCoord width, height;
    wxString wxstrText;
    int iFactor, lpiNum[11];
    iFactor = slide_rule.m_iProduct / slide_rule.m_iFactor;
    lpiNum[0] = slide_rule.m_iFactor / 100;
    lpiNum[1] = (slide_rule.m_iFactor / 10) % 10;
    lpiNum[2] = slide_rule.m_iFactor % 10;
    lpiNum[3] = iFactor / 100;
    lpiNum[4] = (iFactor / 10) % 10;
    lpiNum[5] = iFactor % 10;
    lpiNum[6] = slide_rule.m_iProduct / 10000;
    lpiNum[7] = (slide_rule.m_iProduct / 1000) % 10;
    lpiNum[8] = (slide_rule.m_iProduct / 100) % 10;
    lpiNum[9] = (slide_rule.m_iProduct / 10) % 10;
    lpiNum[10] = slide_rule.m_iProduct % 10;
    wxstrText = wxString::Format
    (
        "%d.%d%d * %d.%d%d = %d.%d%d%d%d",
        lpiNum[0], lpiNum[1], lpiNum[2],
        lpiNum[3], lpiNum[4], lpiNum[5],
        lpiNum[6], lpiNum[7], lpiNum[8],
        lpiNum[9], lpiNum[10]
    );
    dc.SetFont(wxFont(EQUATION_FONT_SIZE, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
    dc.GetTextExtent(wxstrText, &width, &height);
    dc.SetTextForeground(*wxBLACK);
    dc.DrawText(wxstrText, (RULE_LEFT + m_nFrameWidth - width) / 2, (MAIN_RULE_FRAME_TOP - height) / 2);
}

